package byx.container.core;

import byx.container.core.utils.ReflectUtils;

import java.util.function.Consumer;
import java.util.function.Function;

public interface ObjectFactory<T> {
    T create();

    default ObjectFactory<T> singleton() {
        return ObjectFactories.singleton(this);
    }

    default <U> ObjectFactory<U> call(String method, ObjectFactory<?>... params) {
        return ObjectFactories.call(this, method, params);
    }

    default <R> ObjectFactory<R> call(MethodReferences.Function1<R, T> method) {
        return call(ReflectUtils.getMethodName(method));
    }

    default <R, T2> ObjectFactory<R> call(MethodReferences.Function2<R, T, T2> method,
                                          ObjectFactory<T2> p) {
        return call(ReflectUtils.getMethodName(method), p);
    }

    default <R, T2, T3> ObjectFactory<R> call(MethodReferences.Function3<R, T, T2, T3> method,
                                              ObjectFactory<T2> p1 ,ObjectFactory<T3> p2) {
        return call(ReflectUtils.getMethodName(method), p1, p2);
    }

    default <R, T2, T3, T4> ObjectFactory<R> call(MethodReferences.Function4<R, T, T2, T3, T4> method,
                                                  ObjectFactory<T2> p1 ,ObjectFactory<T3> p2, ObjectFactory<T4> p3) {
        return call(ReflectUtils.getMethodName(method), p1, p2, p3);
    }

    default <R, T2, T3, T4, T5> ObjectFactory<R> call(MethodReferences.Function5<R, T, T2, T3, T4, T5> method,
                                                  ObjectFactory<T2> p1 ,ObjectFactory<T3> p2, ObjectFactory<T4> p3, ObjectFactory<T5> p4) {
        return call(ReflectUtils.getMethodName(method), p1, p2, p3, p4);
    }

    default ObjectFactory<T> init(Consumer<T> handler) {
        return ObjectFactories.init(this, handler);
    }

    default ObjectFactory<T> invoke(String method, ObjectFactory<?>... params) {
        return ObjectFactories.invoke(this, method, params);
    }

    default ObjectFactory<T> invoke(MethodReferences.Consumer1<T> method) {
        return invoke(ReflectUtils.getMethodName(method));
    }

    default <T2> ObjectFactory<T> invoke(MethodReferences.Consumer2<T, T2> method,
                                         ObjectFactory<T2> p) {
        return invoke(ReflectUtils.getMethodName(method), p);
    }

    default <T2, T3> ObjectFactory<T> invoke(MethodReferences.Consumer3<T, T2, T3> method,
                                         ObjectFactory<T2> p1, ObjectFactory<T3> p2) {
        return invoke(ReflectUtils.getMethodName(method), p1, p2);
    }

    default <T2, T3, T4> ObjectFactory<T> invoke(MethodReferences.Consumer4<T, T2, T3, T4> method,
                                             ObjectFactory<T2> p1, ObjectFactory<T3> p2, ObjectFactory<T4> p3) {
        return invoke(ReflectUtils.getMethodName(method), p1, p2, p3);
    }

    default <T2, T3, T4, T5> ObjectFactory<T> invoke(MethodReferences.Consumer5<T, T2, T3, T4, T5> method,
                                                 ObjectFactory<T2> p1, ObjectFactory<T3> p2, ObjectFactory<T4> p3, ObjectFactory<T5> p4) {
        return invoke(ReflectUtils.getMethodName(method), p1, p2, p3, p4);
    }

    default <U> ObjectFactory<U> map(Function<T, U> mapper) {
        return ObjectFactories.map(this, mapper);
    }

    default <U> ObjectFactory<U> bind(Function<T, ObjectFactory<U>> binder) {
        return ObjectFactories.bind(this, binder);
    }

    default ObjectFactory<T> guard() {
        return ObjectFactories.guard(this);
    }
}
